package org.loong.crypto.extension.operator.jce;

import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;

import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.operator.ContentVerifier;
import org.bouncycastle.operator.ContentVerifierProvider;
import org.bouncycastle.operator.OperatorCreationException;
import org.loong.crypto.core.algorithm.SignatureAlgorithm;
import org.loong.crypto.core.params.SignatureParameters;
import org.loong.crypto.service.CryptoService;

public class JceContentVerifierProviderBuilder {

    private CryptoService cryptoService;

    public JceContentVerifierProviderBuilder(CryptoService cryptoService) {
        this.cryptoService = cryptoService;
    }

    /**
     * Builder an X.509 certificate verifier provider.
     * 
     * @param publicKey {@link PublicKey}
     * @return {@link ContentVerifierProvider}
     * @throws OperatorCreationException {@link OperatorCreationException}
     */
    public ContentVerifierProvider build(final PublicKey publicKey) throws OperatorCreationException {
        return new ContentVerifierProvider() {

            public boolean hasAssociatedCertificate() {
                return false;
            }

            public X509CertificateHolder getAssociatedCertificate() {
                return null;
            }

            public ContentVerifier get(AlgorithmIdentifier algorithm) throws OperatorCreationException {
                SignatureAlgorithm signatureAlgorithm = null;
                try {
                    signatureAlgorithm = SignatureAlgorithm.findByAlgorithmId(algorithm);
                } catch (NoSuchAlgorithmException e) {
                    throw new OperatorCreationException("No such algorethm [" + algorithm.getAlgorithm().getId() + "] in signature algorithms.", e);
                }

                SignatureParameters params = SignatureParameters.builder().key(publicKey).build();
                return new SigVerifier(signatureAlgorithm, params, new ByteArrayOutputStream());
            }
        };
    }

    private class SigVerifier implements ContentVerifier {

        private SignatureAlgorithm algorithm;

        private SignatureParameters params;

        private ByteArrayOutputStream stream;

        SigVerifier(SignatureAlgorithm algorithm, SignatureParameters params, ByteArrayOutputStream stream) {
            this.algorithm = algorithm;
            this.params = params;
            this.stream = stream;
        }

        public AlgorithmIdentifier getAlgorithmIdentifier() {
            return algorithm.getSigAlgId();
        }

        public OutputStream getOutputStream() {
            if (stream == null) {
                throw new IllegalStateException("verifier not initialised");
            }

            return stream;
        }

        public boolean verify(byte[] expected) {
            return cryptoService.verify(algorithm, params, stream.toByteArray(), expected);
        }
    }
}
